package com.zensun.system.service.impl;

import cn.hutool.core.collection.CollStreamUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zensun.common.annotation.DataScope;
import com.zensun.common.constant.UserConstants;
import com.zensun.common.enums.domain.SysRoleEnum;
import com.zensun.common.utils.Assert;
import com.zensun.system.domain.*;
import com.zensun.system.domain.dto.SysRoleDto;
import com.zensun.system.domain.vo.*;
import com.zensun.system.mapper.*;
import com.zensun.system.service.ISysRoleService;
import com.zensun.system.util.SecurityUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 角色 业务层处理
 *
 * @author gmk
 */
@Service
public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> implements ISysRoleService {
    @Resource
    private SysRoleMapper roleMapper;

    @Resource
    private SysRoleMenuMapper roleMenuMapper;

    @Resource
    private SysUserRoleMapper userRoleMapper;

    @Resource
    private SysRoleDeptMapper roleDeptMapper;

    @Resource
    private SysMenuMapper menuMapper;

    @Resource
    private SysDeptMapper deptMapper;

    /**
     * 根据条件分页查询角色数据
     *
     * @param roleDto 角色信息
     * @return 角色数据集合信息
     */
    @Override
    @DataScope(deptAlias = "d")
    public List<SysRoleVo> selectRoleList(SysRoleDto roleDto) {
        /*
         * Map<String, Object> map = new HashMap<>(); if
         * (!SecurityUtils.isAdmin(SecurityUtils.getLoginUser().getUser().getUserId()))
         * { //如果不是超级管理员增加权限 String deptSql =
         * StrUtil.format(" AND find_in_set( {}, r.ancestors )",
         * SecurityUtils.getLoginUser().getUser().getDeptId()); map.put("dataScope",
         * deptSql); roleDto.setParams(map); }
         */
        return roleMapper.selectRoleList(roleDto);
    }

    /**
     * 新增角色
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public int insertRole(SysRole role) {

        role.setDeptId(SecurityUtils.getDeptId());
        // 1.校验
        Assert.isTrue(!checkRoleNameUnique(role), "新增角色'" + role.getRoleName() + "'失败，角色名称已存在");
        Assert.isTrue(!checkRoleKeyUnique(role), "新增角色'" + role.getRoleName() + "'失败，角色权限已存在");

        // 2.先新增，才有id
        return baseMapper.insert(role);
    }

    /**
     * 修改保存角色
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public int updateRole(SysRole role) {

        //role.setDeptId(SecurityUtils.getDeptId());
        // 1.校验
        checkRoleAllowed(role.getRoleId());
        Assert.isTrue(!checkRoleNameUnique(role), "新增角色'" + role.getRoleName() + "'失败，角色名称已存在");
        Assert.isTrue(!checkRoleKeyUnique(role), "新增角色'" + role.getRoleName() + "'失败，角色权限已存在");

        return baseMapper.updateById(role);
    }

    /**
     * 根据用户ID查询权限
     *
     * @param userId 用户ID
     * @return 权限列表
     */
    @Override
    public Set<String> selectRolePermissionByUserId(Long userId) {
        List<SysRole> perms = roleMapper.selectRolePermissionByUserId(userId);
        Set<String> permsSet = new HashSet<>();
        for (SysRole perm : perms) {
            if (ObjectUtil.isNotNull(perm)) {
                permsSet.addAll(Arrays.asList(perm.getRoleKey().trim().split(",")));
            }
        }
        return permsSet;
    }

    /**
     * 通过角色ID查询角色
     *
     * @param roleId 角色ID
     * @return 角色对象信息
     */
    @Override
    public SysRoleDetailVo selectRoleById(Long roleId) {
        SysRoleDetailVo sysRoleDetailVo = new SysRoleDetailVo();
        // 查询角色详情
        SysRole byId = this.getById(roleId);
        BeanUtils.copyProperties(byId, sysRoleDetailVo);
        // 查询角色拥有的菜单组
        LambdaQueryWrapper<SysRoleMenu> q = new LambdaQueryWrapper<>();
        q.eq(SysRoleMenu::getRoleId, roleId);
        List<SysRoleMenu> sysRoleMenus = roleMenuMapper.selectList(q);
        List<Long> longs = CollStreamUtil.toList(sysRoleMenus, SysRoleMenu::getMenuId);
        sysRoleDetailVo.setMenuIds(longs);
        // 填充角色部门字段
        if (byId.getDataScope() != null && byId.getDataScope().equals("2")) {
            LambdaQueryWrapper<SysRoleDept> lambdaQueryWrapper = new LambdaQueryWrapper<>();
            lambdaQueryWrapper.eq(SysRoleDept::getRoleId, roleId);
            roleDeptMapper.selectList(lambdaQueryWrapper);
            List<Long> longs4DeptIds = CollStreamUtil.toList(roleDeptMapper.selectList(lambdaQueryWrapper),
                    SysRoleDept::getDeptId);
            sysRoleDetailVo.setDeptIds(longs4DeptIds);
        }
        return sysRoleDetailVo;
    }

    @Override
    public SysRoleDataScopeVo selectRoleDataScopeById(Long roleId) {
        SysRoleDataScopeVo sysRoleDataScopeVo = new SysRoleDataScopeVo();
        SysRole byId = this.getById(roleId);
        BeanUtils.copyProperties(byId, sysRoleDataScopeVo);
        // 填充角色部门字段
        LambdaQueryWrapper<SysRoleDept> q = new LambdaQueryWrapper<>();
        q.eq(SysRoleDept::getRoleId, roleId);
        roleDeptMapper.selectList(q);
        List<Long> longs = CollStreamUtil.toList(roleDeptMapper.selectList(q), SysRoleDept::getDeptId);
        sysRoleDataScopeVo.setDeptIds(longs);
        return sysRoleDataScopeVo;
    }

    /**
     * 校验角色名称是否唯一
     *
     * @param role 角色信息
     * @return 结果
     */
    private boolean checkRoleNameUnique(SysRole role) {
        boolean result = true;
        // 增加或者修改
        Long roleId = ObjectUtil.isNull(role.getRoleId()) ? -1L : role.getRoleId();
        // 角色名称
        String roleName = role.getRoleName();
        Long deptId = role.getDeptId();

        // 1.根据角色名称查询数据库是否存在此名称
        LambdaQueryWrapper<SysRole> q = new LambdaQueryWrapper<>();
        q.eq(SysRole::getRoleName, roleName);
        q.eq(SysRole::getDeptId, deptId);
        SysRole one = this.getOne(q);
        if (ObjectUtil.isNotEmpty(one) && !one.getRoleId().equals(roleId)) {
            result = false;
        }
        return result;
    }

    /**
     * 校验角色权限是否唯一
     *
     * @param role 角色信息
     * @return 结果
     */
    private boolean checkRoleKeyUnique(SysRole role) {
        boolean result = true;
        // 增加或者修改
        Long roleId = ObjectUtil.isNull(role.getRoleId()) ? -1L : role.getRoleId();
        // 角色key
        String roleKey = role.getRoleKey();
        Long deptId = role.getDeptId();
        // 1.根据角色key查询数据库是否存在此key
        LambdaQueryWrapper<SysRole> q = new LambdaQueryWrapper<>();
        q.eq(SysRole::getRoleKey, roleKey);
        q.eq(SysRole::getDeptId, deptId);
        SysRole one = this.getOne(q);
        if (ObjectUtil.isNotEmpty(one) && !one.getRoleId().equals(roleId)) {
            result = false;
        }
        return result;
    }

    /**
     * 校验角色是否允许操作
     *
     * @param roleId 角色Id
     */
    public void checkRoleAllowed(Long roleId) {
        Assert.isTrue(ObjectUtil.isNotNull(roleId) && UserConstants.ADMIN_ID.equals(roleId), "不允许操作超级管理员角色");
    }

    /**
     * 修改数据权限信息
     *
     * @param role 角色信息
     * @return 结果
     */
    @Override
    @Transactional
    public Boolean authDataScope(SysRoleDataScopeVo role) {
        // 1.准备数据
        String dataScope = role.getDataScope();
        List<Long> deptIds = role.getDeptIds();
        // 2.校验
        checkRoleAllowed(role.getRoleId());
        Assert.isTrue(!checkDeptExist(deptIds), "绑定失败，部分deptid不存在");

        // 3.删除 角色部门信息
        LambdaQueryWrapper<SysRoleDept> q = new LambdaQueryWrapper<>();
        q.eq(SysRoleDept::getRoleId, role.getRoleId());
        roleDeptMapper.delete(q);
        // 4.增加 角色部门信息
        if (SysRoleEnum.DataScope.DATA_SCOPE_CUSTOM.getCode().equals(dataScope) && CollUtil.isNotEmpty(deptIds)) {
            // 构建增加的角色部门信息
            List<SysRoleDept> sysRoleDeptParam = CollStreamUtil.toList(deptIds, i -> {
                SysRoleDept s = new SysRoleDept();
                s.setRoleId(role.getRoleId());
                s.setDeptId(i);
                return s;
            });
            roleDeptMapper.insertBatch(sysRoleDeptParam);
        }
        // 4.修改角色信息
        SysRole sysRole = new SysRole();
        sysRole.setRoleId(role.getRoleId());
        sysRole.setDataScope(dataScope);
        return this.updateById(sysRole);
    }

    /**
     * 批量删除角色信息
     *
     * @param roleIds 需要删除的角色ID
     * @return 结果
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean deleteRoleByIds(Long[] roleIds) {
        // 1.校验
        List<Long> roleIdList = Arrays.asList(roleIds);
        Assert.isEmpty(roleIdList, "请选择要删除的角色");
        Assert.isTrue(roleIdList.contains(UserConstants.ADMIN_ID), "不能操作超级管理员！");

        LambdaQueryWrapper<SysUserRole> q = new LambdaQueryWrapper<>();
        q.in(SysUserRole::getRoleId, roleIdList);
        List<SysUserRole> sysUserRoles = userRoleMapper.selectList(q);
        Assert.isNotEmpty(sysUserRoles, "您选中的角色有关联的用户，无法删除！");

        // 2.删除角色和菜单关联的数据sys_role_menu
        QueryWrapper<SysRoleMenu> queryWrapper = new QueryWrapper<SysRoleMenu>();
        queryWrapper.in("role_id", roleIdList);
        roleMenuMapper.delete(queryWrapper);

        // 4.删除角色和菜单关联的数据sys_role_dept
        QueryWrapper<SysRoleDept> queryWrapperI = new QueryWrapper<SysRoleDept>();
        queryWrapperI.in("role_id", roleIdList);
        roleDeptMapper.delete(queryWrapperI);

        // 5.删除SysRole
        return baseMapper.deleteBatchIds(roleIdList) > 0 ? true : false;
    }

    @DataScope(deptAlias = "d")
    @Override
    public List<SysRoleSelectVo> optionselect(SysRoleDto sysRoleDto) {
        List<SysRoleVo> sysRoleVos = roleMapper.selectRoleList(sysRoleDto);
        if (CollUtil.isNotEmpty(sysRoleVos)) {
            return sysRoleVos.stream()
                    .filter(sysRole -> !UserConstants.ADMIN_ID.equals(sysRole.getRoleId()))
                    .map(sysRole -> {
                        SysRoleSelectVo vo = new SysRoleSelectVo();
                        BeanUtils.copyProperties(sysRole, vo);
                        return vo;
                    }).collect(Collectors.toList());
        }

        return null;
    }

    @Override
    public int roleBanding4menu(SysRoleMenuIdVo sysRoleMenuIdVo) {
        int result = 0;
        Long roldId = sysRoleMenuIdVo.getRoleId();
        List<Long> menuIds = sysRoleMenuIdVo.getMenuIds();
        // 1.校验
        Assert.isNull(roldId, "绑定失败，角色id不存在");
        Assert.isNull(roleMapper.selectById(roldId), "绑定失败，角色id不存在");
        checkRoleAllowed(roldId);
        Assert.isTrue(!checkMenuExist(menuIds), "绑定失败，部分菜单id不存在");

        // 2.删除角色和菜单关联的数据sys_role_menu
        QueryWrapper<SysRoleMenu> queryWrapper = new QueryWrapper<SysRoleMenu>();
        queryWrapper.eq("role_id", roldId);
        roleMenuMapper.delete(queryWrapper);
        // 3.新增新的菜单ids
        if (ArrayUtil.isNotEmpty(menuIds)) {
            List<SysRoleMenu> sysRoleMenuListParam = CollStreamUtil.toList(menuIds, menuId -> {
                SysRoleMenu s = new SysRoleMenu();
                s.setMenuId(menuId);
                s.setRoleId(roldId);
                return s;
            });
            result = roleMenuMapper.insertBatch(sysRoleMenuListParam);
        }
        return result;
    }

    public boolean checkMenuExist(List<Long> menuIds) {
        boolean result = true;
        if (menuIds != null && menuIds.size() > 0) {
            QueryWrapper<SysMenu> queryWrapper = new QueryWrapper<SysMenu>();
            queryWrapper.in("menu_id", menuIds);
            result = menuMapper.selectCount(queryWrapper) == menuIds.size();
        }
        return result;
    }

    public boolean checkDeptExist(List<Long> deptIds) {
        boolean result = true;
        if (deptIds != null && deptIds.size() > 0) {
            QueryWrapper<SysDept> queryWrapper = new QueryWrapper<SysDept>();
            queryWrapper.in("dept_id", deptIds);
            result = deptMapper.selectCount(queryWrapper) == deptIds.size();
        }
        return result;
    }
}
